package com.atguigu.srb.sms.receiver;

import com.atguigu.srb.base.dto.SmsDTO;
import com.atguigu.srb.rabbitutil.constant.MQConst;
import com.atguigu.srb.sms.service.SmsService;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description:
 * @Author: Gavin
 * @Date: 7/23/2023 4:57 PM
 */
// 消息的监听器
@Component
@Slf4j
public class SmsReceiver {
    @Resource
    SmsService smsService;

    @RabbitListener(bindings =
            {
                    @QueueBinding(
                            //声明队列
                            value = @Queue(value = MQConst.QUEUE_SMS_ITEM, durable = "true"),
                            //声明交换机
                            exchange = @Exchange(value = MQConst.EXCHANGE_TOPIC_SMS
                                    ,type = ExchangeTypes.TOPIC
                                    ,ignoreDeclarationExceptions = Exchange.TRUE),
                            //声明routingKey
                            key = { MQConst.ROUTING_SMS_ITEM }
                    )
            })
    public void send(SmsDTO smsDTO, Message message, Channel channel) throws IOException {


        try {
            log.info("SmsReceiver消息监听,smsDTO是:{},messae是:{},channel是:{}", smsDTO.toString(), message.toString(), channel.toString());
            Map<String, Object> param = new HashMap<>();
            param.put("code", smsDTO.getMessage());
            smsService.sendMsg(smsDTO.getMobile(), smsDTO.getType(), param);
            // 正常情况下,没有异常的话会执行下面的代码
            // 从消息的properties属性里面获取所需要的消息的deliveryTage参数
            // multiple指的是,是否批量确认,通常情况下选择false,不要批量确认,否则存在消息丢失的风险
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            // 到这里面说明正常的业务里面,出现了异常
            if (message.getMessageProperties().getRedelivered()) {
                // 进来这里面说明已经重新入过队列一次了
                // long deliveryTag, boolean multiple,boolean requeue
                // 写法一:channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,false);
                // requeue指的是是否重新入队列,如果这里重新如队列,那么和自动确认就没有什么区别了.遇到异常会疯狂重试
                // 拒绝消息,直接打印错误日志,记录到redis缓存,或者mysql中,如果当前队列有绑定死信队列,那么消息会直接进入死信队列
                channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
            } else {
                // 进来这里面说明是第一次,还没有重新如果队列,所以最后的requeue的值应该是true,让它重新入队列,重试
                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
            }
        }

    }
}
